package ch10
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Rectangle;

	import utils.Ball;

	/**
	 * 角度反弹,修正“线下”问题.
	 * 碰撞检测与边界判断这两种方法都要先确定 ball 与 line 相互接触，
	 * 然后进行坐标旋转获得调整后的位置与速度。如果 ball 的 y 坐标旋转后的位置 y2 超过了 line，则执行反弹。
	 * 但是如果 ball 自下而上穿过了 line，又该怎么办？
	 * 假设 line 在舞台的中心位置，然后 ball 从下面反弹上来。
	 * 如果碰撞检测与边界判断的结果都是 true，Flash 就认为 ball 刚刚从 line 上弹出去，
	 * 它会把 ball 从 line 下面移到上面去。那么我的解决办法是比较 vy1 和 y2，并且只在 vy1 大于 y2 时才进行反弹。
	 * ball 的 y 速度大于 ball 与 line 的相对距离。这就意味着只要它进行运动就会跑到线上面去。
	 * 而 ball 的速度小于 ball 与 line 的相对距离。
	 * 换句话讲，在这一帧内，它位于线下，并且下一帧还是在线下。
	 * @author sjp
	 *
	 */
	public class AngleBounceFinal extends Sprite
	{
		private var ball:Ball;
		private var line:Sprite;
		private var gravity:Number=1;
		private var bounce:Number=-0.9;

		public function AngleBounceFinal()
		{
			init();
		}

		private function init():void
		{
			stage.scaleMode=StageScaleMode.NO_SCALE;
			stage.align=StageAlign.TOP_LEFT;
			ball=new Ball();
			addChild(ball);
			ball.x=350;
			ball.y=100;

			line=new Sprite();
			line.graphics.lineStyle(1);
			line.graphics.lineTo(500, 0);
			addChild(line);
			line.x=50;
			line.y=200;
			line.rotation=30;

			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(event:Event):void
		{
			line.rotation=(stage.stageWidth / 2 - mouseX) * .1;

			// normal motion code
			ball.vy+=gravity;
			ball.x+=ball.vx;
			ball.y+=ball.vy;

			if (ball.x + ball.radius > stage.stageWidth)
			{
				ball.x=stage.stageWidth - ball.radius;
				ball.vx*=bounce;
			}
			else if (ball.x - ball.radius < 0)
			{
				ball.x=ball.radius;
				ball.vx*=bounce;
			}
			if (ball.y + ball.radius > stage.stageHeight)
			{
				ball.y=stage.stageHeight - ball.radius;
				ball.vy*=bounce;
			}
			else if (ball.y - ball.radius < 0)
			{
				ball.y=ball.radius;
				ball.vy*=bounce;
			}

			var bounds:Rectangle=line.getBounds(this);
			if (ball.x > bounds.left && ball.x < bounds.right)
			{

				// get angle, sine and cosine
				var angle:Number=line.rotation * Math.PI / 180;
				var cos:Number=Math.cos(angle);
				var sin:Number=Math.sin(angle);

				// get position of ball, relative to line
				var x1:Number=ball.x - line.x;
				var y1:Number=ball.y - line.y;

				// rotate coordinates
				var y2:Number=cos * y1 - sin * x1;

				// rotate velocity
				var vy1:Number=cos * ball.vy - sin * ball.vx;

				// perform bounce with rotated values
				/**
				 * ball 的 y 速度大于 ball 与 line 的相对距离。这就意味着只要它进行运动就会跑到线上面去。
				 * 而 ball 的速度小于 ball 与 line 的相对距离。
				 * 换句话讲，在这一帧内，它位于线下，并且下一帧还是在线下。
				 * */
				if (y2 > -ball.height / 2 && y2 < vy1)
				{
					// rotate coordinates
					var x2:Number=cos * x1 + sin * y1;

					// rotate velocity
					var vx1:Number=cos * ball.vx + sin * ball.vy;

					y2=-ball.height / 2;
					vy1*=bounce;

					// rotate everything back;
					x1=cos * x2 - sin * y2;
					y1=cos * y2 + sin * x2;
					ball.vx=cos * vx1 - sin * vy1;
					ball.vy=cos * vy1 + sin * vx1;
					ball.x=line.x + x1;
					ball.y=line.y + y1;
				}
			}
		}
	}
}